package org.easyway.gui.base;

import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glTexCoord2f;
import static org.lwjgl.opengl.GL11.glVertex2f;

import java.io.Serializable;

import org.easyway.input.EWMouseAssociation;
import org.easyway.interfaces.extended.IDrawing;
import org.easyway.interfaces.extended.ILoopable;
import org.easyway.interfaces.sprites.IPlain2D;
import org.easyway.lists.BaseList;
import org.easyway.objects.Camera;
import org.easyway.objects.sprites2D.Mask;
import org.easyway.objects.texture.Texture;
import org.easyway.system.StaticRef;

public abstract class GuiContainer extends EWMouseAssociation implements
		IContainer, IDrawing, IPlain2D, Serializable {

	/**
	 * generated serial version id
	 */
	private static final long serialVersionUID = -2001400966226380966L;

	/**
	 * all the children of the container
	 */
	public BaseList<IContainer> children = new BaseList<IContainer>();

	/**
	 * the father of the current container
	 */
	public GuiContainer father;// = Camera.getMainFather();

	/**
	 * coordinates of the window
	 */
	protected float x, y;

	/**
	 * the size of the window
	 */
	protected int width, height;

	/**
	 * coordinates on screen
	 */
	protected float xOnScreen, yOnScreen;

	/**
	 * the idlayer
	 */
	protected int idLayer = -1;

	public static int DEF_IDLAYER = StaticRef.layers.length - 1;

	// -----------------------------------------------
	// -----------------------------------------------

	public GuiContainer() {
		this(0, 0, 0, 0);
	}

	public GuiContainer(String name) {
		this(0, 0, 0, 0, name);
	}

	public GuiContainer(float x, float y) {
		this(x, y, 0, 0);
	}

	public GuiContainer(int width, int height) {
		this(0, 0, width, height);
	}

	public GuiContainer(float x, float y, int width, int height) {
		this(x, y, width, height, "$_CONTAINER");
	}

	public GuiContainer(float x, float y, int width, int height, String name) {
		setType(name);
		setXY(x, y);
		setSize(width, height);
		isDrawable = false;
	}

	
	protected void addToGELists(boolean toAdd) {
		if (!toAdd || !autoAddToLists)
			return;
		if (isLoopable) {
			StaticRef.loopList.add((ILoopable) this);
		}
		// if (father == null)
		// Mouse.addGuiObject(this);
	}

	// -----------------------------------------------
	// -----------------------------------------------

	int layer = 0;

	
	public int getLayer() {
		return layer;
	}

	public void setLayer(int layer) {
		this.layer = layer;
		readdToDrawingLists();
	}

	/**
	 * sets the idlayer of the object
	 * 
	 * @param newIdLayer
	 */
	public void setIdLayer(int newIdLayer) {
		if (idLayer != -1) {
			StaticRef.layers[idLayer].remove(this);
		}

		idLayer = newIdLayer;
		if (idLayer < 0)
			idLayer = 0;
		if (idLayer >= StaticRef.layers.length)
			idLayer = StaticRef.layers.length - 1;
		StaticRef.layers[idLayer].add(this);
	}

	/**
	 * returns the idLayer of the object
	 * 
	 * @return
	 */
	public int getIdLayer() {
		return idLayer;
	}

	// -----------------------------------------------
	// -----------------------------------------------

	
	public void render() {
		if (father == null) {
			draw(0, 0);// getXOnScreen(), getYOnScreen());
		}
	}

	
	public void draw(float x, float y) {
		xOnScreen = x + this.x;
		yOnScreen = y + this.y;
		customDraw();
		children.startScan();
		while (children.next()) {
			children.getCurrent().draw(this.x + x, this.y + y);
		}
	}

	public abstract void customDraw();

	/**
	 * sets a container's father<br>
	 * note: using a 'NULL' father the object will be added to the
	 * defaultFather.<br>
	 * If you want set the father as NULL use the method <b>setNullFather</b>
	 * 
	 * @see #setNullFather()
	 * @param container
	 */
	public void setFather(GuiContainer container) {
		if (container == null)
			container = Camera.getMainFather();
		if (father != null)
			father.removeChild(this);
		father = container;
		// father.addChild(this);
		father.children.add(this);
		// if (container == Camera.getMainFather() || container == null) {
		// if (father != Camera.getMainFather()) {
		// // Mouse.addGuiObject(this);
		// }
		// father = Camera.getMainFather();
		// Camera.getMainFather().addChild(this);
		// } else {
		// container.addChild(this);
		// if (father == Camera.getMainFather()) {
		// // Mouse.removeGuiObject(this);
		// }
		// father = container;
		// }
	}

	public void setNullFather() {
		if (father != null) {
			father.removeChild(this);
			// if (father == Camera.getMainFather()) {
			// // Mouse.removeGuiObject(this);
			// }
		}
		father = null;
	}

	/**
	 * adds this container inside another
	 * 
	 * @param container
	 */
	public void addChild(GuiContainer container) {
		// container.father = this;
		// children.add(container);
		container.setFather(this);
	}

	/**
	 * removes a child from the container
	 * 
	 * @param container
	 */
	public void removeChild(GuiContainer container) {
		container.father = null;
		children.remove(container);
	}

	// ------------------------------------------------
	// ------------ IPLAIN2D --------------------------
	// ------------------------------------------------

	public void incX(float incx) {
		x += incx;
		checkBounds();
	}

	public void incY(float incy) {
		y += incy;
		checkBounds();
	}

	public void move(float incx, float incy) {
		x += incx;
		y += incy;
		checkBounds();
	}

	
	public void setHeight(int height) {
		if (height < 0)
			return;
		this.height = height;
		checkBounds();

		children.startScan();
		while (children.next()) {
			children.getCurrent().checkBounds();
		}
	}

	
	public void setSize(int width, int height) {
		if (width < 0 || height < 0)
			return;
		this.height = height;
		this.width = width;
		checkBounds();

		children.startScan();
		while (children.next()) {
			children.getCurrent().checkBounds();
		}
	}

	
	public void setWidth(int width) {
		if (width < 0)
			return;
		this.width = width;
		checkBounds();

		children.startScan();
		while (children.next()) {
			children.getCurrent().checkBounds();
		}
	}

	
	public void setX(float x) {
		this.x = x;
		checkBounds();
	}

	
	public void setXY(float x, float y) {
		this.x = x;
		this.y = y;
		checkBounds();
	}

	
	public void setY(float y) {
		this.y = y;
		checkBounds();
	}

	public boolean checkBounds() {
		if (father == null)
			return true;
		if (getX() < father.getX())
			x = father.getX();
		if (getX() + getWidth() > father.getX() + father.getWidth())
			x = father.getX() + father.getWidth() - getWidth();
		if (getY() < father.getY())
			y = father.getY();
		if (getY() + getHeight() > father.getY() + father.getHeight())
			y = father.getY() + father.getHeight() - getHeight();
		return false;
	}

	
	public int getHeight() {
		return height;
	}

	
	public int getWidth() {
		return width;
	}

	
	public float getX() {
		if (father == null || father == this)
			return x;
		return x + father.getX();
	}

	
	public float getY() {
		if (father == null || father == this)
			return y;
		return y + father.getY();
	}

	
	public float getXOnScreen() {
		return xOnScreen;
	}

	
	public float getYOnScreen() {
		return yOnScreen;
	}

	protected void drawComponent(float x, float y, Texture image) {
		drawComponent(x, y, image.getWidth(), image.getHeight(), image);
	}

	protected void drawComponent(float x, float y, int width, int height,
			Texture image) {
		image.bind();
		glBegin(GL_QUADS);
		{
			glTexCoord2f(image.getXStart(), image.getYStart());
			glVertex2f(x, y);

			glTexCoord2f(image.getXEnd(), image.getYStart());
			glVertex2f(x + width, y);

			glTexCoord2f(image.getXEnd(), image.getYEnd());
			glVertex2f(x + width, y + height);

			glTexCoord2f(image.getXStart(), image.getYEnd());
			glVertex2f(x, y + height);
		}
		glEnd();
	}

	public void onDestroy() {
		setNullFather();

		// destroy all the children
		children.startScan();
		while (children.next()) {
			children.getCurrent().destroy();
		}
	}

	
	public IContainer getFather() {
		return father;
	}

	// ---------------------------------
	// ---------------------------------
	
	public Mask getMask() {
		return null;
	}

	
	public void onClick(int x, int y) {
	}

	
	public void onDown(int x, int y) {
	}

	
	public void onDrag(int incx, int incy) {
	}

	
	public void onEnter() {
	}

	
	public void onExit() {
	}

	
	public void onOver(int nx, int ny) {
	}

	
	public void onRelease(int x, int y) {
	}

	
	public BaseList<IContainer> getChildren() {
		return children;
	}

	
	public EWMouseAssociation getMouseAssociation() {
		return this;
	}

	public void gainFocus() {
		if (!isOnFocus()) {
			father.children.remove(this);
			father.children.add(this, 0);
		}
	}

	public boolean isOnFocus() {
		return father != null && father.children.get(0) == this;
	}

}
